Skip to content

FiberChannel Multipath for KVM + Pure Flash Array and HPE-Primera Support - #7889

Merged
shwstppr merged 38 commits into
apache:mainfrom
rg9975:primera-pure-storage-provider-feature
Dec 9, 2023
Merged

shwstppr merged 38 commits into
apache:mainfrom
rg9975:primera-pure-storage-provider-feature

Conversation

@rg9975

@rg9975 rg9975 commented Aug 21, 2023

Copy link
Copy Markdown

FiberChannel Multipath SCSI for KVM, Pure Flash Array and HPE-Primera Support

Description

This PR provides a new primary storage volume type called "FiberChannel" that allows access to volumes connected to hosts over fiber channel connections. It requires Multipath to provide path discovery and failover. Second, the PR adds an AdaptivePrimaryDatastoreProvider that abstracts how volumes are managed/orchestrated from the connector to communicate with the primary storage provider, using a ProviderAdapter interface, allowing the code interacting with the primary storage provider API's to be simpler and have no direct dependencies on Cloudstack code. Lastly, the PR provides an implementation of the ProviderAdapter classes for the HP Enterprise Primera line of storage solutions and the Pure Flash Array line of storage solutions.

Types of changes

  • Breaking change (fix or feature that would cause existing functionality to change)
  • New feature (non-breaking change which adds functionality)
  • Bug fix (non-breaking change which fixes an issue)
  • Enhancement (improves an existing feature and functionality)
  • Cleanup (Code refactoring and cleanup, that may add test cases)

Feature/Enhancement Scale or Bug Severity

Feature/Enhancement Scale

  • Major
  • Minor

How Has This Been Tested?

Testing involves the following setup:

  1. An HPE_3PAR A670 deployment with 4 nodes.
  2. A Pure FlashArray FA-X70R2 deployment with 2 nodes.
  3. Two physical servers deployed with Rocky Linux 8.7.
  4. Fiberchannel switching infrastructure providing 4 paths between the physical servers and storage appliances.
  5. Cloudstack zone configured with KVM hypervisor and both physical servers connected.

The following testing scenarios are used:

  1. Create New provider Storage Pool for Zone
  2. Create New provider Storage Pool for Cluster
  3. Update provider Storage Pool for Zone
  4. Update provider Storage Pool for Cluster
  5. Create VM with Root Disk using provider pool
  6. Create VM with Root and Data Disk using provider pool
  7. Create VM with Root Disk using NFS and Data Disk on provider pool
  8. Create VM with Root Disk on provider Pool and Data Disk on NFS
  9. Snapshot root disk with VM using provider Pool for root disk
  10. Snapshot data disk with VM using provider Pool for data disk
  11. Snapshot VM (non-memory) with root and data disk using provider pool
  12. Snapshot VM (non-memory) with root disk using Primera pool and data disk using NFS
  13. Snapshot VM (non-memory) with root disk using NFS pool and data disk using provider pool
  14. Create new template from previous snapshot root disk on provider pool
  15. Create new volume from previous snapshot root disk on provider pool
  16. Create new volume from previous snapshot data disk on provider pool
  17. Create new VM using template created from provider root snapshot and using Primera as root volume pool
  18. Create new VM using template created from provider root snapshot and using NFS as root volume pool
  19. Delete previously created snapshot
  20. Detach a Primera volume from a non-running VM
  21. Attach a Primera volume to a running VM
  22. Attach a Primera volume to a non-running VM
  23. Primera-only: Create a 'thin' Disk Offering tagged for Primera pool and provision and attach a data volume to a VM using this offering (ttpv=true, reduce=false)
  24. Primera-only: Create a 'sparse' Disk Offering tagged for Primera pool and provision and attach a data volume to a VM using this offering (ttpv=false, reduce=true)
  25. Primera-only: Create a 'fat' Disk Offering and tagged for Primera pool and provision and attach a data volume to a VM using this offering (should fail as 'fat' not supported)
  26. Perform volume migration of root volume from provider pool to NFS pool on stopped VM
  27. Perform volume migration of root volume from NFS pool to provider pool on stopped VM
  28. Perform volume migration of data volume from provider pool to NFS pool on stopped VM
  29. Perform volume migration of data volume from NFS pool to provider pool on stopped VM
  30. Perform VM data migration for a VM with 1 or more data volumes from all volumes on provider pool to all volumes on NFS pool
  31. Perform VM data migration for a VM with 1 or more data volumes from all volumes on NFS pool to all volumes on provider pool
  32. Perform live migration of a VM with a provider root disk
  33. Perform live migration of a VM with a provider data disk and NFS root disk
  34. Perform live migration of a VM with a provider root disk and NFS data disk
  35. Perform volume migration between 2 provider pools on the same backend provider IP address
  36. Perform volume migration between 2 provider pools on different provider IP address
  37. Perform volume migration from 1 provider to another provider and start/confirm with VM.
  38. Perform volume migration back from 2nd provider to 1st provider and start/confirm with VM.

@boring-cyborg

boring-cyborg Bot commented Aug 21, 2023

Copy link
Copy Markdown

Congratulations on your first Pull Request and welcome to the Apache CloudStack community! If you have any issues or are unsure about any anything please check our Contribution Guide (https://github.com/apache/cloudstack/blob/main/CONTRIBUTING.md)
Here are some useful points:

@weizhouapache

Copy link
Copy Markdown
Member

great work @rg9975

@DaanHoogland

Copy link
Copy Markdown
Contributor

huge job with great description. looking forward to reviewing the code @rg9975 !

@DaanHoogland
DaanHoogland requested a review from slavkap August 22, 2023 08:28
@DaanHoogland

Copy link
Copy Markdown
Contributor

@blueorangutan package

@blueorangutan

Copy link
Copy Markdown

@DaanHoogland a [SF] Jenkins job has been kicked to build packages. It will be bundled with KVM, XenServer and VMware SystemVM templates. I'll keep you posted as I make progress.

@blueorangutan

Copy link
Copy Markdown

Packaging result [SF]: ✔️ el7 ✔️ el8 ✔️ el9 ✔️ debian ✔️ suse15. SL-JID 6847

@DaanHoogland

Copy link
Copy Markdown
Contributor

@blueorangutan test

@DaanHoogland DaanHoogland added this to the 4.19.0.0 milestone Aug 22, 2023
@blueorangutan

Copy link
Copy Markdown

@DaanHoogland a [SF] Trillian-Jenkins test job (centos7 mgmt + kvm-centos7) has been kicked to run smoke tests

@blueorangutan

Copy link
Copy Markdown

[SF] Trillian test result (tid-7515)
Environment: kvm-centos7 (x2), Advanced Networking with Mgmt server 7
Total time taken: 44143 seconds
Marvin logs: https://github.com/blueorangutan/acs-prs/releases/download/trillian/pr7889-t7515-kvm-centos7.zip
Smoke tests completed. 112 look OK, 1 have errors, 0 did not run
Only failed and skipped tests results shown below:

Test Result Time (s) Test File
test_08_upgrade_kubernetes_ha_cluster Failure 713.10 test_kubernetes_clusters.py

@yadvr

yadvr commented Sep 7, 2023

Copy link
Copy Markdown
Member

@rg9975 can you check the build failure in the GitHub actions job?

@DaanHoogland

Copy link
Copy Markdown
Contributor

@blueorangutan package

@blueorangutan

Copy link
Copy Markdown

@DaanHoogland a [SF] Jenkins job has been kicked to build packages. It will be bundled with KVM, XenServer and VMware SystemVM templates. I'll keep you posted as I make progress.

@codecov

codecov Bot commented Sep 18, 2023 •

Copy link
Copy Markdown

Codecov Report

Attention: 3579 lines in your changes are missing coverage. Please review.

Comparison is base (d3cad42) 29.15% compared to head (b58a5db) 28.47%.
Report is 6 commits behind head on main.

Files Patch % Lines
...atastore/adapter/flasharray/FlashArrayAdapter.java 0.00% 535 Missing ⚠️
...rage/datastore/adapter/primera/PrimeraAdapter.java 0.00% 484 Missing ⚠️
.../datastore/driver/AdaptiveDataStoreDriverImpl.java 1.82% 430 Missing ⚠️
...pervisor/kvm/storage/MultipathSCSIAdapterBase.java 0.00% 369 Missing ⚠️
...orage/datastore/adapter/primera/PrimeraVolume.java 0.00% 203 Missing ⚠️
...tore/lifecycle/AdaptiveDataStoreLifeCycleImpl.java 2.35% 166 Missing ⚠️
...torage/motion/StorageSystemDataMotionStrategy.java 0.00% 113 Missing ⚠️
.../storage/datastore/adapter/primera/PrimeraCpg.java 0.00% 76 Missing ⚠️
...datastore/adapter/ProviderAdapterDiskOffering.java 0.00% 70 Missing ⚠️
...datastore/adapter/flasharray/FlashArrayVolume.java 0.00% 70 Missing ⚠️
... and 72 more
Additional details and impacted files
@@             Coverage Diff              @@
##               main    #7889      +/-   ##
============================================
- Coverage     29.15%   28.47%   -0.69%     
+ Complexity    31278    30732     -546     
============================================
  Files          5251     5319      +68     
  Lines        368763   372351    +3588     
  Branches      53759    54168     +409     
============================================
- Hits         107529   106018    -1511     
- Misses       246530   251802    +5272     
+ Partials      14704    14531     -173     
Flag Coverage Δ
simulator-marvin-tests 24.24% <1.84%> (-0.80%) ⬇️
uitests 4.44% <0.00%> (-0.01%) ⬇️
unit-tests 14.75% <0.03%> (-0.16%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@blueorangutan

Copy link
Copy Markdown

Packaging result [SF]: ✔️ el7 ✔️ el8 ✔️ el9 ✔️ debian ✔️ suse15. SL-JID 7041

@DaanHoogland

Copy link
Copy Markdown
Contributor

@yadvr

yadvr commented Oct 4, 2023

Copy link
Copy Markdown
Member

@blueorangutan package

@blueorangutan

Copy link
Copy Markdown

@rohityadavcloud a [SF] Jenkins job has been kicked to build packages. It will be bundled with KVM, XenServer and VMware SystemVM templates. I'll keep you posted as I make progress.

@DaanHoogland DaanHoogland reopened this Oct 4, 2023
@blueorangutan

Copy link
Copy Markdown

@DaanHoogland a [SL] Jenkins job has been kicked to build packages. It will be bundled with KVM, XenServer and VMware SystemVM templates. I'll keep you posted as I make progress.

@blueorangutan

Copy link
Copy Markdown

Packaging result [SF]: ✔️ el7 ✔️ el8 ✔️ el9 ✖️ debian ✔️ suse15. SL-JID 7951

@shwstppr

shwstppr commented Dec 6, 2023

Copy link
Copy Markdown
Contributor

@blueorangutan test

@blueorangutan

Copy link
Copy Markdown

@shwstppr a [SL] Trillian-Jenkins test job (centos7 mgmt + kvm-centos7) has been kicked to run smoke tests

@blueorangutan

Copy link
Copy Markdown

[SF] Trillian test result (tid-8495)
Environment: kvm-centos7 (x2), Advanced Networking with Mgmt server 7
Total time taken: 34947 seconds
Marvin logs: https://github.com/blueorangutan/acs-prs/releases/download/trillian/pr7889-t8495-kvm-centos7.zip
Smoke tests completed. 110 look OK, 11 have errors, 0 did not run
Only failed and skipped tests results shown below:

Test Result Time (s) Test File
ContextSuite context=TestDeployVM>:setup Error 0.00 test_vm_life_cycle.py
test_01_secure_vm_migration Error 0.02 test_vm_life_cycle.py
test_02_unsecure_vm_migration Error 0.01 test_vm_life_cycle.py
test_03_secured_to_nonsecured_vm_migration Error 0.01 test_vm_life_cycle.py
test_04_nonsecured_to_secured_vm_migration Error 0.01 test_vm_life_cycle.py
ContextSuite context=TestVMLifeCycle>:setup Error 3.15 test_vm_life_cycle.py
ContextSuite context=TestVMSchedule>:setup Error 0.00 test_vm_schedule.py
ContextSuite context=TestVmSnapshot>:setup Error 3.10 test_vm_snapshots.py
test_04_deploy_vnf_appliance Error 93.36 test_vnf_templates.py
test_04_deploy_vnf_appliance Error 93.36 test_vnf_templates.py
test_05_delete_vnf_template Error 0.07 test_vnf_templates.py
ContextSuite context=TestVnfTemplates>:teardown Error 1.15 test_vnf_templates.py
ContextSuite context=TestCreateVolume>:setup Error 0.00 test_volumes.py
test_01_root_volume_encryption Error 0.01 test_volumes.py
test_02_data_volume_encryption Error 0.01 test_volumes.py
test_03_root_and_data_volume_encryption Error 0.01 test_volumes.py
ContextSuite context=TestVolumes>:setup Error 4.30 test_volumes.py
test_01_verify_ipv6_vpc Error 3.31 test_vpc_ipv6.py
test_01_create_redundant_VPC_2tiers_4VMs_4IPs_4PF_ACL Error 5.23 test_vpc_redundant.py
test_02_redundant_VPC_default_routes Error 5.16 test_vpc_redundant.py
test_03_create_redundant_VPC_1tier_2VMs_2IPs_2PF_ACL_reboot_routers Error 5.12 test_vpc_redundant.py
test_04_rvpc_network_garbage_collector_nics Error 5.08 test_vpc_redundant.py
test_05_rvpc_multi_tiers Error 5.06 test_vpc_redundant.py
test_01_VPC_nics_after_destroy Error 4.04 test_vpc_router_nics.py
test_02_VPC_default_routes Error 4.07 test_vpc_router_nics.py
test_01_redundant_vpc_site2site_vpn Failure 4.38 test_vpc_vpn.py
test_01_vpc_site2site_vpn_multiple_options Failure 3.32 test_vpc_vpn.py
test_01_vpc_remote_access_vpn Failure 1.14 test_vpc_vpn.py
test_01_vpc_site2site_vpn Failure 3.31 test_vpc_vpn.py
test_01_cancel_host_maintenace_with_no_migration_jobs Error 0.05 test_host_maintenance.py
test_02_cancel_host_maintenace_with_migration_jobs Error 0.04 test_host_maintenance.py
test_03_cancel_host_maintenace_with_migration_jobs_failure Error 0.04 test_host_maintenance.py
test_01_cancel_host_maintenance_ssh_enabled_agent_connected Error 0.01 test_host_maintenance.py
test_03_cancel_host_maintenance_ssh_disabled_agent_connected Error 0.01 test_host_maintenance.py
test_04_cancel_host_maintenance_ssh_disabled_agent_disconnected Error 0.01 test_host_maintenance.py
test_hostha_enable_ha_when_host_in_maintenance Error 334.38 test_hostha_kvm.py

@yadvr yadvr left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM - let's merge this as/when it passes regression tests; we can't test HPE-Primera and rely on the author's own testing.

@yadvr

yadvr commented Dec 7, 2023

Copy link
Copy Markdown
Member

@blueorangutan package

@blueorangutan

Copy link
Copy Markdown

@rohityadavcloud a [SL] Jenkins job has been kicked to build packages. It will be bundled with KVM, XenServer and VMware SystemVM templates. I'll keep you posted as I make progress.

@blueorangutan

Copy link
Copy Markdown

Packaging result [SF]: ✔️ el7 ✔️ el8 ✔️ el9 ✔️ debian ✔️ suse15. SL-JID 7961

@DaanHoogland

Copy link
Copy Markdown
Contributor

@blueorangutan test

@blueorangutan

Copy link
Copy Markdown

@DaanHoogland a [SL] Trillian-Jenkins test job (centos7 mgmt + kvm-centos7) has been kicked to run smoke tests

@blueorangutan

Copy link
Copy Markdown

[SF] Trillian test result (tid-8498)
Environment: kvm-centos7 (x2), Advanced Networking with Mgmt server 7
Total time taken: 38932 seconds
Marvin logs: https://github.com/blueorangutan/acs-prs/releases/download/trillian/pr7889-t8498-kvm-centos7.zip
Smoke tests completed. 109 look OK, 12 have errors, 0 did not run
Only failed and skipped tests results shown below:

Test Result Time (s) Test File
test_11_destroy_ssvm Failure 912.88 test_ssvm.py
test_list_system_vms_metrics_history Failure 0.18 test_metrics_api.py
ContextSuite context=TestDeployVM>:setup Error 0.00 test_vm_life_cycle.py
test_01_secure_vm_migration Error 0.02 test_vm_life_cycle.py
test_02_unsecure_vm_migration Error 0.01 test_vm_life_cycle.py
test_03_secured_to_nonsecured_vm_migration Error 0.01 test_vm_life_cycle.py
test_04_nonsecured_to_secured_vm_migration Error 0.01 test_vm_life_cycle.py
ContextSuite context=TestVMLifeCycle>:setup Error 3.18 test_vm_life_cycle.py
ContextSuite context=TestVMSchedule>:setup Error 0.00 test_vm_schedule.py
ContextSuite context=TestVmSnapshot>:setup Error 3.24 test_vm_snapshots.py
test_04_deploy_vnf_appliance Error 93.53 test_vnf_templates.py
test_04_deploy_vnf_appliance Error 93.53 test_vnf_templates.py
test_05_delete_vnf_template Error 0.05 test_vnf_templates.py
ContextSuite context=TestVnfTemplates>:teardown Error 0.13 test_vnf_templates.py
ContextSuite context=TestCreateVolume>:setup Error 0.00 test_volumes.py
test_01_root_volume_encryption Error 0.02 test_volumes.py
test_02_data_volume_encryption Error 0.01 test_volumes.py
test_03_root_and_data_volume_encryption Error 0.01 test_volumes.py
ContextSuite context=TestVolumes>:setup Error 4.36 test_volumes.py
test_01_verify_ipv6_vpc Error 3.33 test_vpc_ipv6.py
test_01_create_redundant_VPC_2tiers_4VMs_4IPs_4PF_ACL Error 4.24 test_vpc_redundant.py
test_02_redundant_VPC_default_routes Error 4.21 test_vpc_redundant.py
test_03_create_redundant_VPC_1tier_2VMs_2IPs_2PF_ACL_reboot_routers Error 4.13 test_vpc_redundant.py
test_04_rvpc_network_garbage_collector_nics Error 5.22 test_vpc_redundant.py
test_05_rvpc_multi_tiers Error 4.28 test_vpc_redundant.py
test_01_VPC_nics_after_destroy Error 3.17 test_vpc_router_nics.py
test_02_VPC_default_routes Error 4.14 test_vpc_router_nics.py
test_01_redundant_vpc_site2site_vpn Failure 3.38 test_vpc_vpn.py
test_01_vpc_site2site_vpn_multiple_options Failure 3.38 test_vpc_vpn.py
test_01_vpc_remote_access_vpn Failure 1.14 test_vpc_vpn.py
test_01_vpc_site2site_vpn Failure 3.35 test_vpc_vpn.py
test_01_cancel_host_maintenace_with_no_migration_jobs Error 0.05 test_host_maintenance.py
test_02_cancel_host_maintenace_with_migration_jobs Error 0.04 test_host_maintenance.py
test_03_cancel_host_maintenace_with_migration_jobs_failure Error 0.04 test_host_maintenance.py
test_01_cancel_host_maintenance_ssh_enabled_agent_connected Error 0.01 test_host_maintenance.py
test_03_cancel_host_maintenance_ssh_disabled_agent_connected Error 0.01 test_host_maintenance.py
test_04_cancel_host_maintenance_ssh_disabled_agent_disconnected Error 0.01 test_host_maintenance.py

@DaanHoogland

Copy link
Copy Markdown
Contributor

i'm insane
@blueorangutan test

@blueorangutan

Copy link
Copy Markdown

@DaanHoogland a [SL] Trillian-Jenkins test job (centos7 mgmt + kvm-centos7) has been kicked to run smoke tests

@blueorangutan

Copy link
Copy Markdown

[SF] Trillian test result (tid-8524)
Environment: kvm-centos7 (x2), Advanced Networking with Mgmt server 7
Total time taken: 51775 seconds
Marvin logs: https://github.com/blueorangutan/acs-prs/releases/download/trillian/pr7889-t8524-kvm-centos7.zip
Smoke tests completed. 119 look OK, 2 have errors, 0 did not run
Only failed and skipped tests results shown below:

Test Result Time (s) Test File
test_08_migrate_vm Error 44.91 test_vm_life_cycle.py
test_01_redundant_vpc_site2site_vpn Failure 386.79 test_vpc_vpn.py

@shwstppr
shwstppr merged commit 1031c31 into apache:main Dec 9, 2023
dhslove pushed a commit to ablecloud-team/ablestack-cloud that referenced this pull request Dec 11, 2023
…port (apache#7889)

This PR provides a new primary storage volume type called "FiberChannel" that allows access to volumes connected to hosts over fiber channel connections. It requires Multipath to provide path discovery and failover. Second, the PR adds an AdaptivePrimaryDatastoreProvider that abstracts how volumes are managed/orchestrated from the connector to communicate with the primary storage provider, using a ProviderAdapter interface, allowing the code interacting with the primary storage provider API's to be simpler and have no direct dependencies on Cloudstack code. Lastly, the PR provides an implementation of the ProviderAdapter classes for the HP Enterprise Primera line of storage solutions and the Pure Flash Array line of storage solutions.
dhslove added a commit to ablecloud-team/ablestack-cloud that referenced this pull request Dec 11, 2023
dhslove pushed a commit to ablecloud-team/ablestack-cloud that referenced this pull request Jan 17, 2024
…port (apache#7889)

This PR provides a new primary storage volume type called "FiberChannel" that allows access to volumes connected to hosts over fiber channel connections. It requires Multipath to provide path discovery and failover. Second, the PR adds an AdaptivePrimaryDatastoreProvider that abstracts how volumes are managed/orchestrated from the connector to communicate with the primary storage provider, using a ProviderAdapter interface, allowing the code interacting with the primary storage provider API's to be simpler and have no direct dependencies on Cloudstack code. Lastly, the PR provides an implementation of the ProviderAdapter classes for the HP Enterprise Primera line of storage solutions and the Pure Flash Array line of storage solutions.
@rg9975
rg9975 deleted the primera-pure-storage-provider-feature branch March 31, 2024 01:37
@meletisf

Copy link
Copy Markdown

Hello, are there any plans to support Alletra 5/6K and MP? This is probably compatible with 9K since it is based on Primera but with the general direction being the MP platform (officially GreenLake for Block) it would be interesting to see support for the new generations.

@rg9975

rg9975 commented May 22, 2024

Copy link
Copy Markdown
Author

Hello, are there any plans to support Alletra 5/6K and MP? This is probably compatible with 9K since it is based on Primera but with the general direction being the MP platform (officially GreenLake for Block) it would be interesting to see support for the new generations.

At this time we do not have plans to test against these newer devices, but the Alletra line appears to support HPE Web Services API 1.10 which the driver is coded to. I suspect it may work as-is or with minor tweaks.

@meletisf

meletisf commented May 22, 2024 •

Copy link
Copy Markdown

Hello, are there any plans to support Alletra 5/6K and MP? This is probably compatible with 9K since it is based on Primera but with the general direction being the MP platform (officially GreenLake for Block) it would be interesting to see support for the new generations.

At this time we do not have plans to test against these newer devices, but the Alletra line appears to support HPE Web Services API 1.10 which the driver is coded to. I suspect it may work as-is or with minor tweaks.

Good point about the Web Services API. I will raise it to the Alletra engineering team to verify it. Most likely i will test it on A6K and MP arrays and let you know!

@knowanand

Copy link
Copy Markdown

Hello,

We’re using a 3PAR 8000 series array with API version 1.6.

Although the documentation mentions support for 3par/Primera, we cannot integrate our existing 3par storage setup with CloudStack.

Is there a workaround or method for integrating our current 3PAR infrastructure without making major changes to the storage layer?
It just stays on the error that I posted in the image.

image

We have tried both with

http://admin:pass@172..xx.xx.xx:8008/api/v1?cpg=cloudstack&hostset=kvm-cs&capacitybytes=4000000000000

and

http://admin:pass@172.xx.xx.xx:8008/api/v1?cpg=cloudstack&hostset=kvm-cs

Irrespective of whether the capacity bytes are in the API URL, or in the capacity bytes section of the UI, it always comes back with

Request failed. (530)
Failed to add data store: Capacity bytes not available from the storage provider, user provided capacity bytes must be specified

on 3par CLI it shows that the user is connected:

fs9000 cli% showwsapi
-Service- -State- -HTTP_State- HTTP_Port -HTTPS_State- HTTPS_Port -Version- ------------API_URL-------------
Enabled Active Enabled 8008 Enabled 8080 1.6.5 https://172.xx.xx.xx:8080/api/v1
fs9000 cli%

@rg9975

rg9975 commented May 6, 2025 •

Copy link
Copy Markdown
Author

I'm unsure why you would be seeing that. The driver reads the capacityBytes from the provided inputs and, if its set, will validate it does not exceed the value from the arrays capacityInBytes. When it is not set, it will attempt to get the capacityInBytes from the array (if set) and use it or log the exception you are seeing. You perhaps can try using a CMK command to see if there is something happening between the UI and API when providing the inputs? you may also be able to set the capacityInBytes on the array (assuming the version of the Web Service API your using supports that). We have only tested on the HPE Web Service v1.10+.

            ProviderVolumeStorageStats stats = api.getManagedStorageStats();
            if (capacityBytes != null && capacityBytes != 0 && stats != null) {
                if (stats.getCapacityInBytes() > 0) {
                    if (stats.getCapacityInBytes() < capacityBytes) {
                        throw new InvalidParameterValueException("Capacity bytes provided exceeds the capacity of the storage endpoint: provided by user: " + capacityBytes + ", storage capacity from storage provider: " + stats.getCapacityInBytes());
                    }
                }
                parameters.setCapacityBytes(capacityBytes);
            }
            // if we have no user-provided capacity bytes, use the ones provided by storage
            else {
                if (stats == null || stats.getCapacityInBytes() <= 0) {
                    throw new InvalidParameterValueException("Capacity bytes not available from the storage provider, user provided capacity bytes must be specified");
                }
                parameters.setCapacityBytes(stats.getCapacityInBytes());
            }

@knowanand

Copy link
Copy Markdown

I'm working with a 3PAR system where the API version is 1.6.5. I understand some methods might differ with this version, but since I’m entering capacity manually through the UI, I assume that part should still work as expected.

As for CMK, I installed the EXE and connected using credentials, but I’m a bit stuck on how to add the primary 3PAR storage to a zone from the CLI. I haven’t found clear guidance on this yet, so any help or pointers would be really appreciated!

Thanks in advance!

genegr pushed a commit to genegr/cloudstack that referenced this pull request Sep 24, 2026
Resize of a volume on an NVMe-TCP pool previously failed. The provider half
worked -- FlashArrayAdapter.resize() PATCHes the array to grow the namespace --
but the KVM half was never wired up:

  * LibvirtResizeVolumeCommandWrapper short-circuits MultipathSCSIPool via
    handleMultipathSCSIResize(), but MultipathNVMeOFPool implements
    KVMStoragePool directly rather than extending MultipathSCSIPool, so it
    fell through.
  * It then reached LibvirtComputingResource.getResizeScriptType(), which has
    no NVMeTCP case, and threw
    "Cannot determine resize type from pool type NVMeTCP".
  * MultipathNVMeOFAdapterBase.resize() threw UnsupportedOperationException.

Wire up the host-side half, mirroring how the Fibre Channel path is handled
(FC shipped resize in its own initial PR, apache#7889):

  * LibvirtResizeVolumeCommandWrapper: dispatch MultipathNVMeOFPool to a new
    handleMultipathNVMeOFResize(), alongside the existing SCSI branch.
  * MultipathNVMeOFPool.resize(): delegate to the adaptor, mirroring
    MultipathSCSIPool.resize().
  * MultipathNVMeOFAdapterBase.resize(): issue nvme ns-rescan so the kernel
    observes the grown namespace without waiting for an asynchronous event
    notification from the target, poll until the new size is visible, then
    virsh blockresize so a running guest sees the capacity without a reboot.

Unlike the SCSI path there is no device-mapper map to grow, so no helper
script is needed; the existing rescanAllControllers() is reused.

If the namespace does not report the new size within the settle timeout we
log a warning rather than throwing: the array has already been grown at that
point, and failing here would make the management server roll back a resize
that actually happened.

The guest-notification lookup matches the domain disk source against the
/dev/disk/by-id symlink, its canonicalised /dev/nvmeXnY target, or the bare
EUI, because libvirt may report any of those forms.

Signed-off-by: Eugenio Grosso <eugenio.grosso@gmail.com>
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
genegr pushed a commit to genegr/cloudstack that referenced this pull request Sep 25, 2026
When Fibre Channel managed storage was added (apache#7889) the FiberChannel pool
type was added to a number of allowlists in shared managed-storage code. The
NVMe-TCP work added the pool type, the KVM adaptor and the lifecycle pivot but
not those allowlists, so several managed-storage operations silently did not
work on an NVMe-TCP pool even though the equivalent Fibre Channel operation
did.

StorageSystemDataMotionStrategy:

  * verifyFormatWithPoolType(): RAW was accepted only on PowerFlex and
    FiberChannel, so any data-motion of a RAW NVMe-TCP volume was rejected
    outright. Message updated to match.
  * handleCreateTemplateFromManagedVolume(): same format check, which made
    createTemplate from an NVMe-TCP volume fail with the (misleading)
    "When using managed storage, you can only create a template from a volume
    on KVM currently."
  * the grantAccess() / revokeAccess() pair around the template copy only ran
    for a detached volume or for PowerFlex/FiberChannel, so an attached
    NVMe-TCP volume was never granted host access for the copy. Both sites are
    updated together so access is still revoked in the finally block.
  * getSnapshotDetails(): PowerFlex and FiberChannel pass the snapshot path
    straight through as the device address, which is also correct for
    array-native NVMe-TCP snapshots.

ApiDBUtils.getHypervisorTypeFromZone(): NVMeTCP is a KVM-only pool type, so
its presence in a zone should imply KVM in the same way the other non-QCOW2
pool types already do.

StatsCollector's volume-stats format check deliberately not touched: RAW is
already in its accepted-format list, so the guard never fires for NVMe-TCP
volumes and adding the pool type there would be dead weight.

Found by working through the test matrix @abh1sar asked for on
apache#13061, which surfaced these as a family rather than one at
a time.

Signed-off-by: Eugenio Grosso <eugenio.grosso@gmail.com>
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
nvazquez pushed a commit that referenced this pull request Oct 26, 2026
…orage (#13061)

* adapter: add NVMETCP address type and FlashArrayConnection.nsid

Preparatory data-model changes for NVMe-TCP support on the adaptive
storage framework. No behaviour change for existing Fibre Channel
users - the extra enum value, field, and getter/setter are only
exercised by callers that explicitly use them.

ProviderVolume.AddressType gains a NVMETCP value alongside FIBERWWN,
so adapters can declare that a volume is addressed by an NVMe EUI-128
(NGUID) rather than a SCSI WWN.

FlashArrayVolume.getAddress() produces the NGUID layout expected by
the Linux kernel for a FlashArray NVMe namespace:

    00 + serial[0:14] + 24a937 (Pure 6-hex OUI) + serial[14:24]

which matches the /dev/disk/by-id/nvme-eui.<id> symlink emitted by
udev. Fibre Channel callers (addressType != NVMETCP) still get the
existing 6 + 24a9370 + serial form.

FlashArrayConnection gains a nsid field to carry the namespace id the
FlashArray REST API attaches to host-group-scoped NVMe connections,
when it is present.

* flasharray: support NVMe-TCP transport

Teach FlashArrayAdapter to talk to a pool over NVMe over TCP instead of
Fibre Channel.

The transport is selected from a new transport= option on the storage
pool URL (or the equivalent storage_pool_details entry), e.g.

    https://user:pass@fa:443/api?pod=cs&transport=nvme-tcp&hostgroup=cluster1

Defaults remain Fibre Channel / WWN addressing when transport is absent
or anything other than nvme-tcp, so existing FC pools are unaffected.

Beyond the transport parsing itself the adapter now:

  * Tracks a per-pool volumeAddressType (AddressType.NVMETCP or
    FIBERWWN) and stamps every volume it hands back to the framework
    with it (withAddressType), so the adaptive driver path stores the
    correct type=... field in the CloudStack volume path (used later
    by the KVM driver to locate the device).

  * Attaches pod-backed NVMe-TCP volumes at the host-group level
    (POST /connections?host_group_names=...) instead of per-host, so
    the array assigns a consistent NSID to every member host; falls
    back to per-host attach for FC or when no hostgroup is configured.

  * Tolerates a missing nsid in the FlashArray connections response
    for NVMe-TCP - Purity does not return one for host-group NVMe
    connections; the namespace is identified on the host by EUI-128
    from FlashArrayVolume.getAddress(), so a placeholder value is
    returned to the caller purely for informational tracking.

  * Resolves NVMETCP addresses back to volumes in getVolumeByAddress
    by reversing the EUI-128 layout (strip optional eui. prefix, drop
    leading 00 and the embedded Pure OUI).

  * Indexes NVMe connections in getConnectionIdMap by host name (the
    array returns one entry per host inside a host-group connection),
    so connid.<hostname> tokens in the path still match in
    parseAndValidatePath on the KVM side.

Followed by a matching adaptive/KVM driver change (separate commit).

* storage: add NVMeTCP storage pool type

NVMe-oF over TCP (NVMe-TCP) is conceptually a separate storage fabric
from Fibre Channel / iSCSI: it speaks the NVMe command set rather than
SCSI, identifies namespaces by EUI-128 NGUIDs rather than WWNs, and on
Linux is multipathed natively by the nvme driver rather than by
device-mapper multipath. Giving it its own StoragePoolType lets the
KVM agent dispatch the adaptive driver to a dedicated NVMe-oF adapter
(added in the next commit) without polluting the existing Fibre Channel
code path.

The new value is wired into the same format-routing and derivePath
fall-through paths that already special-case FiberChannel in
KVMStorageProcessor: NVMe-TCP volumes are also RAW and carry their
device path in DataObjectTO.path rather than in a managedStoreTarget
detail.

* kvm: add MultipathNVMeOFAdapterBase and NVMeTCPAdapter

Introduce an NVMe-over-Fabrics counterpart to the existing
MultipathSCSIAdapterBase / FiberChannelAdapter pair.

NVMe-oF is conceptually distinct from SCSI - it speaks the NVMe command
set, identifies namespaces by EUI-128 NGUIDs, and is multipathed by the
kernel natively rather than by device-mapper - so keeping it out of the
SCSI code path avoids special-casing inside every method that handles
volume paths, connect, disconnect, or size lookup.

MultipathNVMeOFAdapterBase (abstract)

  * Parses volume paths of the form
        type=NVMETCP; address=<eui>; connid.<host>=<nsid>; ...
    into an AddressInfo whose path is
        /dev/disk/by-id/nvme-eui.<eui>
    which is the udev symlink the kernel emits for every NVMe namespace.

  * connectPhysicalDisk polls the udev path and, on every iteration,
    triggers nvme ns-rescan on all local NVMe controllers, to cover
    target/firmware combinations that do not send an asynchronous event
    notification when a new namespace is mapped.

  * disconnectPhysicalDisk is a no-op; the kernel drops the namespace
    when the target removes the host-group connection. The
    ByPath variant only claims paths starting with
    /dev/disk/by-id/nvme-eui. so foreign paths still fall through to
    other adapters.

  * Delegates getPhysicalDisk, isConnected, and getPhysicalDiskSize to
    plain test -b / blockdev --getsize64 calls - no SCSI rescan, no dm
    multipath, no multipath-map cleanup timer.

  * createPhysicalDisk / createTemplateFromDisk / listPhysicalDisks /
    copyPhysicalDisk all throw UnsupportedOperationException - these
    are the responsibility of the storage provider, not the KVM
    adapter, same as the SCSI base.

MultipathNVMeOFPool

  * KVMStoragePool mirror of MultipathSCSIPool. Defaults to
    Storage.StoragePoolType.NVMeTCP in the parameterless-fallback
    constructor.

NVMeTCPAdapter

  * Concrete adapter that registers itself for
    Storage.StoragePoolType.NVMeTCP via the reflection-based scan in
    KVMStoragePoolManager. Carries no logic of its own beyond binding
    the base to the pool type.

A similar MultipathNVMeOFAdapterBase-derived NVMeRoCEAdapter (or
NVMeFCAdapter) can later be added by adding one concrete subclass and a
new pool-type value; the base does not assume any particular
fabric-level transport.

* adaptive: pick NVMeTCP pool type when transport=nvme-tcp

The adaptive storage framework hard-coded FiberChannel as the KVM-side
pool type for every provider it fronts. With a separate NVMeTCP pool
type now available (and a dedicated NVMe-oF adapter on the KVM side),
teach the lifecycle to route a pool to the right adapter based on a
transport= URL parameter:

  https://user:pass@host/api?...&transport=nvme-tcp

  -> StoragePoolType.NVMeTCP -> NVMeTCPAdapter on the KVM host

When the query parameter is absent the default stays FiberChannel, so
existing FC deployments on Primera or FlashArray continue to work
unchanged.

The choice is made in the shared AdaptiveDataStoreLifeCycleImpl rather
than inside each vendor plugin so every adaptive provider (FlashArray,
Primera, any future one) speaks the same configuration vocabulary.

* docs: note NVMe-TCP support on the FlashArray adaptive plugin in PendingReleaseNotes

* kvm: implement copyPhysicalDisk on MultipathNVMeOFAdapterBase

The NVMe-oF KVM adapter refused every template copy request from the
adaptive storage orchestrator with UnsupportedOperationException, which
made it impossible to use an NVMe-TCP pool as primary storage for a VM
root disk: every deploy that landed a root volume on the pool failed
as soon as CloudStack tried to lay down the template.

Implement it the same way FiberChannel (SCSI) does: the storage provider
creates and connects a raw namespace ahead of time, then the adapter
resolves the host-side /dev/disk/by-id/nvme-eui.<NGUID> path via the
existing getPhysicalDisk plumbing (which will nvme ns-rescan and wait
for the symlink if the kernel has not yet picked it up) and qemu-img
converts the source image into the raw block device.

User-space encrypted source or destination volumes are rejected: the
FlashArray already encrypts at rest and layering qemu-img LUKS on top
of a hostgroup-scoped namespace shared between hosts is not a sensible
layering. Source encryption would also break on migration because the
passphrase does not travel.

With this change a CloudStack KVM VM can have its ROOT volume on an
NVMe-TCP pool (tested end-to-end on 4.23-SNAPSHOT against Purity 6.7.7:
template copy, first boot, live migrate with data disk, VM snapshot
with quiesce, and revert all work).

Signed-off-by: Eugenio Grosso <eugenio.grosso@gmail.com>

* kvm/flasharray: address review feedback on NVMe-TCP PR

Apply the review comments from the first round on #13061:

* FlashArrayAdapter.snapshot() and both getSnapshot() entry points now
  wrap the returned FlashArrayVolume in withAddressType(). Without this,
  snapshots taken against an NVMe-TCP pool had the constructor-default
  AddressType.FIBERWWN and ProviderSnapshot.getAddress() emitted an FC
  style WWN instead of the NVMe EUI-128, which the adaptive driver then
  persisted as the snapshot path. Verified end-to-end against Purity 6.7.7:
  a fresh NVMe-TCP snapshot now lands with install_path starting 006c... ,
  matching the source volume's EUI (previously it was 6-24a9370...).

* FlashArrayAdapter.attach() - retry path after 'Connection already
  exists' no longer requires a hostgroup-scoped match for NVMe-TCP. If
  hostgroup is not configured, or the existing connection is host-scoped,
  fall back to matching by host name, same as the Fibre Channel branch.
  Also normalize the 'volume lun is not found' message when no
  connection list is returned.

* FlashArrayAdapter.attach() - initial 'Volume attach did not return lun
  information' exception message now mentions both lun (FC) and nsid
  (NVMe-TCP) so the error is not misleading on NVMe deployments.

* FlashArrayAdapter.getVolumeByAddress() - validate the EUI-128 length
  before slicing. A short/malformed address used to throw
  StringIndexOutOfBoundsException deep inside getFlashArrayItem and be
  swallowed as 'not found'; now a clear RuntimeException is raised with
  the expected vs actual length.

* FlashArrayVolume.getAddress() - same defensive check when building an
  EUI-128 from the FlashArray volume serial; if the serial is shorter
  than 24 hex chars, fail with a clear message instead of SIOOBE.

* MultipathNVMeOFAdapterBase.connectPhysicalDisk() - Integer.parseInt of
  the STORAGE_POOL_DISK_WAIT detail is now guarded; a non-numeric value
  falls back to the default rather than aborting the connect.

* MultipathNVMeOFAdapterBase.rescanAllControllers() - honour the boolean
  return from Process.waitFor(). If an nvme ns-rescan invocation does
  not complete in NS_RESCAN_TIMEOUT_SECS we destroyForcibly() it, so
  hung nvme-cli processes do not accumulate while the namespace poll
  loop retries.

* NVMeTCPAdapter - rename LOGGER_NVMETCP to LOGGER to match the naming
  convention used in the other KVM adapters.

Signed-off-by: Eugenio Grosso <eugenio.grosso@gmail.com>

* ui: expose NVMe-TCP transport for FlashArray primary storage

AddPrimaryStorage previously pinned the protocol to FiberChannel whenever the operator picked the FlashArray provider, leaving NVMe-TCP backends only reachable by hand-crafting the URL with ?transport=nvme-tcp. Surface the choice in the form:

- protocols dropdown for FlashArray now offers FiberChannel and NVMeTCP (Primera stays FC-only).

- when NVMeTCP is selected, the submit handler appends transport=nvme-tcp to the FlashArray URL so the adaptive lifecycle pivot in pickPoolType() resolves StoragePoolType.NVMeTCP server-side.

- the generic Path field, already hidden for FiberChannel, is also hidden for NVMeTCP for parity.

Signed-off-by: Eugenio Grosso <eugenio.grosso@gmail.com>

* kvm: align MultipathNVMeOFPool with KVMStoragePool API change

Reported by @slavkap: building this branch against current main produces a compile error in MultipathNVMeOFPool because the KVMStoragePool interface drifted between when this PR was last rebased (2026-05-22) and main today (2026-06-09):

- checkingHeartBeat(HAStoragePool, HostTO) was renamed to hasHeartBeat(HAStoragePool, HostTO)

- vmActivityCheck(...) was renamed to hasVmActivity(...) with the same signature

Update the two overrides accordingly (both still return null as before — MultipathNVMeOFPool does not participate in the KVM-side HA heartbeat). Mirrors how MultipathSCSIPool implements them.

Verified compile via mvn -pl plugins/hypervisors/kvm -am -DskipTests compile.

Signed-off-by: Eugenio Grosso <eugenio.grosso@gmail.com>

* kvm: guard parseAndValidatePath against null inPath

@slavkap noted that parseAndValidatePath has no internal null guard. The two callers in this base class (getPhysicalDisk and connectPhysicalDisk) already pre-check via StringUtils.isEmpty(volumePath), so the current code path is safe, but as a public method it deserves defense-in-depth.

Throw CloudRuntimeException with a clear message instead of letting it NPE on inPath.split(...). Mirrors how the FC sibling FiberChannelAdapter throws on an invalid address type.

Signed-off-by: Eugenio Grosso <eugenio.grosso@gmail.com>

* kvm/flasharray/ui: address Copilot review round 3 on NVMe-TCP PR

Address the 2026-07-27 Copilot review comments:

- AddPrimaryStorage.vue: set (or replace) the transport= query parameter
  instead of blindly appending it, so a FlashArray URL that already carries
  a hand-entered transport= is not left with two conflicting values.

- FlashArrayVolume: slice the EUI-128 serial with explicit substring(14, 24)
  rather than substring(14). The length guard rejected short serials but a
  longer-than-expected serial would previously yield an EUI over 32 hex
  characters, which cannot match /dev/disk/by-id/nvme-eui.<eui>.

- FlashArrayAdapter: guard the Fibre Channel connection match against
  hostnames with no dot. hostname.substring(0, hostname.indexOf('.')) would
  throw StringIndexOutOfBoundsException when indexOf('.') == -1. Mirrors the
  guard already present on the NVMe-TCP branch.

- MultipathNVMeOFAdapterBase: MapStorageUuidToStoragePool is now a
  ConcurrentHashMap populated via computeIfAbsent, so concurrent callers
  cannot race to create duplicate pool objects or corrupt the map.

- MultipathNVMeOFAdapterBase: split key=value tokens with a limit of 2 so a
  value containing '=' is no longer silently discarded.

- MultipathNVMeOFAdapterBase: throttle rescanAllControllers() during
  namespace discovery to once per 10s instead of on every 2s poll. Spawning
  one nvme ns-rescan per controller every 2s is bursty on hosts with many
  controllers; the first iteration still rescans immediately.

No functional change to the happy path.

Signed-off-by: Eugenio Grosso <eugenio.grosso@gmail.com>

* flasharray: tighten NVMe-TCP EUI-128 validation

Address the 2026-08-04 Copilot follow-up:

- FlashArrayVolume.getAddress() now requires the serial to match exactly 24
  hexadecimal characters instead of merely rejecting shorter ones. The previous
  form sliced serial[0:24], so two distinct serials sharing a 24-character
  prefix would have produced the same EUI-128 and therefore the same volume
  identity. Non-hex serials are now rejected as well.

- FlashArrayAdapter.getVolumeByAddress() validates the FlashArray EUI-128
  layout before reversing it into a serial: 32 hexadecimal characters, a 00
  prefix, and the Pure Storage OUI at offset 16. Previously any 32-character
  string was accepted and deterministically mapped onto a volume serial, so a
  malformed or tampered address could resolve to an unintended volume.

Verified against a real FlashArray namespace: EUI
006c1b16ce1c034d24a9371c05ab334a passes both checks and round-trips to serial
6C1B16CE1C034D1C05AB334A and back unchanged.

Signed-off-by: Eugenio Grosso <eugenio.grosso@gmail.com>

* kvm: implement volume resize for NVMe-TCP pools

Resize of a volume on an NVMe-TCP pool previously failed. The provider half
worked -- FlashArrayAdapter.resize() PATCHes the array to grow the namespace --
but the KVM half was never wired up:

  * LibvirtResizeVolumeCommandWrapper short-circuits MultipathSCSIPool via
    handleMultipathSCSIResize(), but MultipathNVMeOFPool implements
    KVMStoragePool directly rather than extending MultipathSCSIPool, so it
    fell through.
  * It then reached LibvirtComputingResource.getResizeScriptType(), which has
    no NVMeTCP case, and threw
    "Cannot determine resize type from pool type NVMeTCP".
  * MultipathNVMeOFAdapterBase.resize() threw UnsupportedOperationException.

Wire up the host-side half, mirroring how the Fibre Channel path is handled
(FC shipped resize in its own initial PR, #7889):

  * LibvirtResizeVolumeCommandWrapper: dispatch MultipathNVMeOFPool to a new
    handleMultipathNVMeOFResize(), alongside the existing SCSI branch.
  * MultipathNVMeOFPool.resize(): delegate to the adaptor, mirroring
    MultipathSCSIPool.resize().
  * MultipathNVMeOFAdapterBase.resize(): issue nvme ns-rescan so the kernel
    observes the grown namespace without waiting for an asynchronous event
    notification from the target, poll until the new size is visible, then
    virsh blockresize so a running guest sees the capacity without a reboot.

Unlike the SCSI path there is no device-mapper map to grow, so no helper
script is needed; the existing rescanAllControllers() is reused.

If the namespace does not report the new size within the settle timeout we
log a warning rather than throwing: the array has already been grown at that
point, and failing here would make the management server roll back a resize
that actually happened.

The guest-notification lookup matches the domain disk source against the
/dev/disk/by-id symlink, its canonicalised /dev/nvmeXnY target, or the bare
EUI, because libvirt may report any of those forms.

Signed-off-by: Eugenio Grosso <eugenio.grosso@gmail.com>
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* server: allow online volume resize on NVMe-TCP pools

validateVolumeResizeWithSize() refuses to resize a volume on a managed pool
while its VM is running, unless the pool type is on an allowlist. That list
held PowerFlex and FiberChannel but not NVMeTCP, so resizing an attached
NVMe-TCP volume on a running KVM instance failed at the API layer with
"This kind of KVM disk cannot be resized while it is connected to a VM
that's not in the Stopped state." before any host-side work was attempted.

Add NVMeTCP alongside FiberChannel. The two transports are equivalent here:
in both cases the provider grows the volume on the array and the KVM agent
then makes the new capacity visible to the guest, which the preceding commit
implements for NVMe-oF.

Found by running the test matrix @abh1sar requested on #13061:
offline resize (VM Stopped) already worked end to end -- CloudStack, the array
and the guest all agreed on the new size -- and this guard was the only thing
blocking the online case.

Signed-off-by: Eugenio Grosso <eugenio.grosso@gmail.com>
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* storage: recognise NVMeTCP in the managed-storage code paths

When Fibre Channel managed storage was added (#7889) the FiberChannel pool
type was added to a number of allowlists in shared managed-storage code. The
NVMe-TCP work added the pool type, the KVM adaptor and the lifecycle pivot but
not those allowlists, so several managed-storage operations silently did not
work on an NVMe-TCP pool even though the equivalent Fibre Channel operation
did.

StorageSystemDataMotionStrategy:

  * verifyFormatWithPoolType(): RAW was accepted only on PowerFlex and
    FiberChannel, so any data-motion of a RAW NVMe-TCP volume was rejected
    outright. Message updated to match.
  * handleCreateTemplateFromManagedVolume(): same format check, which made
    createTemplate from an NVMe-TCP volume fail with the (misleading)
    "When using managed storage, you can only create a template from a volume
    on KVM currently."
  * the grantAccess() / revokeAccess() pair around the template copy only ran
    for a detached volume or for PowerFlex/FiberChannel, so an attached
    NVMe-TCP volume was never granted host access for the copy. Both sites are
    updated together so access is still revoked in the finally block.
  * getSnapshotDetails(): PowerFlex and FiberChannel pass the snapshot path
    straight through as the device address, which is also correct for
    array-native NVMe-TCP snapshots.

ApiDBUtils.getHypervisorTypeFromZone(): NVMeTCP is a KVM-only pool type, so
its presence in a zone should imply KVM in the same way the other non-QCOW2
pool types already do.

StatsCollector's volume-stats format check deliberately not touched: RAW is
already in its accepted-format list, so the guard never fires for NVMe-TCP
volumes and adding the pool type there would be dead weight.

Found by working through the test matrix @abh1sar asked for on
#13061, which surfaced these as a family rather than one at
a time.

Signed-off-by: Eugenio Grosso <eugenio.grosso@gmail.com>
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* flasharray: connect NVMe-TCP volumes per host, not per host group

Access to a managed volume is granted and revoked one host at a time:
AdaptiveDataStoreDriverImpl.grantAccess() calls attach(volume, hostname) and
revokeAccess() calls detach(volume, hostname). Neither the adapter context nor
the data object carries the VM or its power state, so the adapter cannot tell
why access is being revoked.

attach() did not follow that model for NVMe-TCP. When a hostgroup was
configured it created a single host-group scoped connection shared by every
member host, while detach() still ran per host and deleted that shared
connection. The two are irreconcilable, and live migration shows why:

  grantAccess(V, dst)   -> POST /connections?host_group_names=cluster1  ("already exists")
  ... guest migrates and is now running on dst, doing I/O to V ...
  revokeAccess(V, src)  -> DELETE /connections?host_group_names=cluster1

The DELETE removes the only connection there is, so V is revoked from the whole
group including dst, the namespace disappears under the running guest, and with
werror=stop the VM freezes.

Group scoping was justified in a comment as giving "a consistent NSID visible to
every member host", but the NSID is not used to find the namespace: it is
located by its EUI-128 address (FlashArrayVolume.getAddress()), as the comment
20 lines further down already says. So the justification does not hold.

Connect and disconnect per host instead, which is what Fibre Channel has always
done in this same adapter and what the interface can express. The hostgroup
parameter remains accepted and is now used only where it was documented to be
used -- removing leftover group connections when a volume is deleted -- matching
the "retrieve for legacy purposes" comment on the field. In the "Connection
already exists" path a group-scoped match is demoted to a fallback so volumes
still carrying a group connection from an earlier release keep resolving.

Found while working through the test matrix @abh1sar asked for on
#13061, as the cause of the live-migration failure.

Signed-off-by: Eugenio Grosso <eugenio.grosso@gmail.com>
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* flasharray: always stamp the pool address type on returned volumes

FlashArrayVolume defaults addressType to FIBERWWN in its constructor, so a
volume deserialised from the array reports an FC-style NAA WWN from getAddress()
until the pool's real address type is stamped onto it. Most accessors do that
via withAddressType(), but two paths returned an unstamped volume:

  * revert() returned the private getVolume() result directly, so reverting a
    snapshot on an NVMe-TCP pool produced a volume carrying an FC-style WWN.
  * copy() returned the newly created volume unstamped. The adaptive driver
    persists this one -- copyAsync() stores generatePathInfo(outVolume, ...) --
    so the volume was recorded in the database with an address the host cannot
    resolve, since NVMe namespaces are located by EUI-128.

Rather than add a third withAddressType() call at each site, stamp inside the
private getVolume() accessor so every path is correct by construction. That
matches the private getSnapshot() accessor, which already did this, and makes
the wrap at the public getVolume() call site redundant. copy() now reuses the
accessor for its source lookup instead of repeating the same GET inline, and
stamps the volume it hands back.

No behavioural change for Fibre Channel: FIBERWWN is what the constructor
already defaulted to, so stamping it is a no-op there.

Also drops a comment in getConnectionIdMap() describing connections as
host-group scoped, which is no longer how NVMe-TCP volumes are connected after
the preceding commit. The surrounding logic was already keyed on host name and
is unchanged; only nsid vs lun differs between the transports.

Found by auditing every NVMe-TCP divergence from the Fibre Channel path, on the
principle that NVMe-TCP is only a different carrier: the array operations are
identical and the host-side difference is an NQN instead of a WWN, which lives
on the array and is never touched by this adapter (hosts are resolved by name).

Signed-off-by: Eugenio Grosso <eugenio.grosso@gmail.com>
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* kvm: align NVMe-oF adaptor stubs with the Fibre Channel adaptor

NVMe-TCP is a different carrier for the same array operations, so the NVMe-oF
storage adaptor should only diverge from MultipathSCSIAdapterBase where the
transport genuinely differs. Several unimplemented methods threw
UnsupportedOperationException where the Fibre Channel adaptor returns a benign
value, which means a caller that degrades gracefully on Fibre Channel aborts on
NVMe-TCP:

  * deletePhysicalDisk(): FC returns false. Namespaces are created and destroyed
    by the storage provider, so a host-side delete is simply not handled here;
    throwing from a cleanup path is worse than reporting that.
  * createTemplateFromDisk(), listPhysicalDisks(): FC returns null.
  * createFolder(): FC logs and returns true. Block storage has no directory
    structure, so this is a no-op rather than an error.

createPhysicalDisk(), createDiskFromTemplate() and createDiskFromTemplateBacking()
still throw, because the Fibre Channel adaptor throws for those too.

createTemplateFromDirectDownloadFile() also still throws, but the message now
states the limitation instead of reading as an oversight. The Fibre Channel
implementation resolves the downloaded file with destPool.getPhysicalDisk(),
which on this adaptor requires a "type=NVMETCP;address=..." path and therefore
cannot describe a plain local file. Supporting it needs a separate way to present
the local file as the copy source, which is left for follow-up rather than
guessed at here.

No functional change to the connect/disconnect paths. The comment on
disconnectPhysicalDisk() is updated because it described connections as
host-group scoped, which is no longer how NVMe-TCP volumes are connected, and to
record why the host-side no-op is correct: the kernel drops the namespace when
the target removes the connection, whereas Fibre Channel must flush its
device-mapper entry.

Signed-off-by: Eugenio Grosso <eugenio.grosso@gmail.com>
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* kvm: support direct download templates on NVMe-oF pools

createTemplateFromDirectDownloadFile() threw UnsupportedOperationException, so a
template registered with directdownload=true could not be deployed onto an
NVMe-TCP pool: the download itself succeeded and the deployment then failed on
the host.

The two path arguments are different kinds of thing, and conflating them is what
made this look harder than it is. templateFilePath is a plain local file produced
by the direct-download helper, while destTemplatePath is a managed volume path of
the form "type=NVMETCP;address=...". Only the destination may be resolved through
KVMStoragePool.getPhysicalDisk(); handing the local file to that would pass a file
name to parseAndValidatePath() and fail. KVMStorageProcessor has already issued
connectPhysicalDisk() for the destination by the time we are called, so the
namespace is present.

Worth recording that the Fibre Channel implementation does conflate them. It calls
destPool.getPhysicalDisk(templateFilePath), and FiberChannelAdapter's
parseAndValidatePath() treats any string without a ";" as a bare WWN, so a local
file path becomes "/dev/mapper/3" + that path and the lookup cannot succeed. This
implementation follows ScaleIOStorageAdaptor instead, which is the working
precedent for block-backed managed storage.

The template is written as QCOW2 onto the raw namespace rather than as RAW. Three
reasons: it matches ScaleIO; it agrees with the QCOW2 format the template is
registered with, so stored content and declared format do not contradict each
other; and KVMStorageProcessor runs Qcow2Inspector.validateQcow2File() on the path
we return, which for a block device that exists would fail on RAW content and then
try to delete the device.

The disk returned to the caller is constructed rather than being the one
getPhysicalDisk() handed back, because its *name* matters and not just its path.
KVMStorageProcessor puts disk.getName() into the DirectDownloadAnswer, which
becomes the template's install path in template_spool_ref and later its external
name, and the provider interpolates that external name into array REST calls.
getPhysicalDisk() names a disk AddressInfo.toString(), e.g.

  AddressInfo NVMETCP [address=006c..., connectionId=2172, path=/dev/disk/by-id/...]

Recording that as the install path makes the first deployment from a freshly
registered template fail: revokeAccess() -> detach() builds
"/connections?host_names=kvm01&volume_names=cloudstack2::<external name>" and
URI.create() rejects the spaces and brackets with "Illegal character in query".
The surfaced error is a misleading "Failed to update state", because the resulting
CloudRuntimeException is followed by applying OperationFailed to a template that
has already reached Ready. Naming the returned disk with the managed volume path
gives the same install path shape a volume records:

  type=NVMETCP; address=006c...; providerName=cloudstack2::tpl-9-0-2-210; providerID=...

Also handles compressed templates via TemplateDownloaderUtil, since
direct-download templates are commonly published that way, and refuses up front if
the template will not fit the namespace rather than failing part way through the
convert.

Tested on a two-host KVM cluster against a FlashArray over NVMe-TCP: registered a
QCOW2 template with directdownload=true and deployed a VM from it, on the first
attempt, with a storage offering tagged for the NVMe-TCP pool:

  ROOT-32  Everpure X90 NVMe-TCP pool2  Ready
           type=NVMETCP; address=006c1b16ce1c034d24a9371c0623b125

Signed-off-by: Eugenio Grosso <eugenio.grosso@gmail.com>
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

---------

Signed-off-by: Eugenio Grosso <eugenio.grosso@gmail.com>
Co-authored-by: Eugenio Grosso <egrosso@purestorage.com>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

9 participants